use std::path::PathBuf;
use std::sync::Arc;
use std::fs;
use thiserror::Error;
use glob::glob;
use agent::generic::ProviderConfig;
use common::compose::{CallBox, Composer, FError, ListHandler};
use markdown::create_html_to_markdown;
use trans::trans_provider::{create_translator, TargetLang};
use log::{info, debug, error};

/// HTML处理器错误类型
#[derive(Debug, Error)]
pub enum HtmlProcessorError {
    /// 文件读取错误
    #[error("文件读取错误: {0}")]
    IoError(#[from] std::io::Error),

    /// Glob模式错误
    #[error("Glob模式错误: {0}")]
    GlobError(#[from] glob::PatternError),

    /// Glob匹配错误
    #[error("Glob匹配错误: {0}")]
    GlobMatchError(#[from] glob::GlobError),
}

impl From<HtmlProcessorError> for FError {
    fn from(e: HtmlProcessorError) -> Self {
        FError::Generic(Box::new(e))
    }
}


/// 读取指定目录中的文件
fn read_files(dir_path: &str, pattern: &str) -> Result<Vec<(PathBuf, String)>, HtmlProcessorError> {
    info!("开始读取文件从目录: {}", dir_path);
    let pattern = format!("{}/{}", dir_path, pattern);
    debug!("使用glob模式: {}", pattern);
    let mut files = Vec::new();

    for entry in glob(&pattern)? {
        let path = entry?;
        debug!("发现文件: {}", path.display());
        let content = std::fs::read_to_string(&path)?;
        debug!("成功读取文件, 大小: {} 字节", content.len());
        files.push((path, content));
    }

    info!("共读取到 {} 个文件", files.len());
    Ok(files)
}

/// 创建一个读取指定模式文件的CallBox
fn make_read_file_callbox<'a>(pattern: &'a str) -> CallBox<'a, String, Vec<(PathBuf, String)>> {
    Composer::from_fn(async move|dir_path: String| {
        let result = read_files(&dir_path, pattern)?;
        Ok(result)
    })
}

fn make_write_file_callbox(output_dir: String, suffix: String) -> CallBox<'static, (PathBuf, String), (PathBuf, String)> {
    Composer::from_fn(move |(path, content): (PathBuf, String)| {
        let output_dir = output_dir.clone();
        let suffix = suffix.clone();

        async move {
            debug!("开始保存文件: {}", path.display());

            // 生成输出文件名
            let output_dir = PathBuf::from(&output_dir);

            // 创建输出目录
            fs::create_dir_all(&output_dir).map_err(|e| FError::Generic(Box::new(e)))?;

            // 获取原文件名
            let file_stem = path.file_stem()
                .ok_or_else(|| FError::Generic(Box::new(std::io::Error::new(
                    std::io::ErrorKind::NotFound,
                    format!("无法获取文件名干: {}", path.display())
                ))))?;
            debug!("文件名干: {}", file_stem.to_string_lossy());

            // 新的输出文件路径
            let output_file = output_dir.join(format!("{}{}", file_stem.to_string_lossy(), suffix));

            // 保存文件
            fs::write(&output_file, content.clone()).map_err(|e| FError::Generic(Box::new(e)))?;
            info!("文件保存成功: {}", output_file.display());
            Ok((path, content))
        }
    })
}

/// 创建一个处理HTML文件夹的CallBox
///
/// 该CallBox接收一个文件夹路径作为输入，读取该文件夹中的所有HTML文件，
/// 将每个HTML文件转换为Markdown，然后翻译成中文，并将结果保存到指定的输出目录。
///
/// # 参数
///
/// * `html_to_md_provider` - 用于HTML到Markdown转换的提供商
/// * `html_to_md_model` - 用于HTML到Markdown转换的模型
/// * `translator_provider` - 用于翻译的提供商
/// * `translator_model` - 用于翻译的模型
/// * `parallel_num` - 并行处理的文件数量
/// * `output_dir` - 输出目录，处理结果将保存到该目录
///
/// # 返回值
///
/// 返回一个CallBox，该CallBox接收一个字符串（文件夹路径），返回处理的文件数量
pub fn create_html_folder_processor(
    html_to_md_provider_config: ProviderConfig,
    html_to_md_model: &str,
    translator_provider_config: ProviderConfig,
    translator_model: &str,
    parallel_num: usize,
    output_dir: String,
) -> CallBox<'static, String, usize> {
    info!("创建 HTML 文件夹处理器");
    debug!("并行处理数量: {}", parallel_num);
    debug!("输出目录: {}", output_dir);

    // 创建HTML到Markdown的转换器
    let html_to_md = create_html_to_markdown(
        Some((html_to_md_provider_config, html_to_md_model)),
    );

    // 创建翻译器
    let translator = create_translator(
        translator_provider_config,
        translator_model,
        TargetLang::Zh,
    );

    // 第一步：读取HTML文件
    let read_html_files_callbox = make_read_file_callbox("**/*.html");

    // 翻译后的Markdown的目录
    let zh_output_dir = format!("{}/zh", &output_dir);
    let write_md_callbox = make_write_file_callbox(zh_output_dir, ".md".to_string());

    // 处理单个文件的CallBox
    let process_single = Composer::compose(
        Composer::compose_parallel_base(
            Composer::from_fn(async |path: PathBuf| {
                info!("开始处理文件: {}", path.display());
                Ok(path)
            }),
            Composer::compose(html_to_md, translator)
        ),
        write_md_callbox
    );

    // 组合所有CallBox
    let complete_callbox = Composer::compose3(
        read_html_files_callbox,
        ListHandler::new(parallel_num, process_single),
        Composer::from_fn(async |v: Vec<_>| {
            info!("处理完成, 共处理 {} 个文件", v.len());
            Ok(v.len())
        })
    );

    info!("创建 HTML 文件夹处理器完成");
    return complete_callbox;
}

/// 创建一个处理HTML文件夹的CallBox，先将HTML转换为Markdown，保存后再批量翻译
///
/// 该CallBox接收一个文件夹路径作为输入，读取该文件夹中的所有HTML文件，
/// 将每个HTML文件转换为Markdown并保存，然后批量翻译成中文，并将结果保存到指定的输出目录。
///
/// # 参数
///
/// * `html_to_md_provider_config` - 用于HTML到Markdown转换的提供商配置
/// * `html_to_md_model` - 用于HTML到Markdown转换的模型
/// * `translator_provider_config` - 用于翻译的提供商配置
/// * `translator_model` - 用于翻译的模型
/// * `parallel_num` - 并行处理的文件数量
/// * `output_dir` - 输出目录，处理结果将保存到该目录
///
/// # 返回值
///
/// 返回一个CallBox，该CallBox接收一个字符串（文件夹路径），返回处理的文件数量
pub fn create_html_folder_processor2(
    html_to_md_provider_config: ProviderConfig,
    html_to_md_model: &str,
    translator_provider_config: ProviderConfig,
    translator_model: &str,
    parallel_num: usize,
    output_dir: String,
) -> CallBox<'static, String, usize> {
    info!("创建 HTML 文件夹处理器2 (先转换后翻译)");
    debug!("并行处理数量: {}", parallel_num);
    debug!("输出目录: {}", output_dir);

    // 创建HTML到Markdown的转换器
    let html_to_md = create_html_to_markdown(
        Some((html_to_md_provider_config, html_to_md_model)),
    );

    // 创建翻译器
    let translator = create_translator(
        translator_provider_config,
        translator_model,
        TargetLang::Zh,
    );

    // 第一步：读取HTML文件
    let read_html_files_callbox = make_read_file_callbox("**/*.html");

    // 输出Markdown的目录
    let md_output_dir = format!("{}/md", &output_dir);
    let write_md_callbox = make_write_file_callbox(md_output_dir, ".md".to_string());

    // 翻译后的Markdown的目录
    let zh_output_dir = format!("{}/zh", &output_dir);
    let trans_md_callbox = make_write_file_callbox(zh_output_dir, ".md".to_string());

    let single_handler = Composer::compose(
        Composer::compose(
            Composer::compose_parallel_base(
                Composer::identity(),
                 html_to_md
            ),
            write_md_callbox
        ),
        Composer::compose(
            Composer::compose_parallel_base(
                Composer::identity(),
                translator
            ),
            trans_md_callbox
        )
    );

    let complete_callbox = Composer::compose3(
        read_html_files_callbox,
        ListHandler::new(parallel_num, single_handler),
        Composer::from_fn(async |v: Vec<_>| {
            info!("处理完成, 共处理 {} 个文件", v.len());
            Ok(v.len())
        })
    );

    return complete_callbox;
}


